package com.yyoa.dao.impl;

import java.lang.reflect.ParameterizedType;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import org.apache.commons.lang.StringUtils;
import org.hibernate.Criteria;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.CriteriaSpecification;
import org.hibernate.criterion.Criterion;
import org.hibernate.criterion.Junction;
import org.hibernate.criterion.MatchMode;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Projection;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Restrictions;
import org.hibernate.internal.CriteriaImpl;
import org.hibernate.transform.ResultTransformer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.Assert;

import com.yyoa.dao.BaseDao;
import com.yyoa.model.Constraint;
import com.yyoa.model.ConstraintGroup;
import com.yyoa.model.Page;
import com.yyoa.model.SimpleConstraint;
import com.yyoa.util.ReflectionUtils;

@SuppressWarnings({ "unchecked", "rawtypes" })
public class BaseDaoImpl<T> implements BaseDao<T> {

	protected Logger logger = LoggerFactory.getLogger(getClass());

	@Autowired
	private SessionFactory sessionFactory;
	protected Class<T> entityClass;

	public BaseDaoImpl() {


	}

	protected Class getEntityClass() {
		if (entityClass == null) {
			entityClass = (Class<T>) ((ParameterizedType) getClass()
					.getGenericSuperclass()).getActualTypeArguments()[0];
		}
		return entityClass;
	}

	/**
	 * <保存实体> <完整保存实体>
	 * 
	 * @param t
	 *            实体参数
	 * @see com.yyoa.testData.BaseDaoTest.launcher.util.BaseDao#save(java.lang.Object)
	 */
	@Override
	public void save(T t) {
		this.getSession().save(t);
	}

	/**
	 * <保存或者更新实体>
	 * 
	 * @param t
	 *            实体
	 * @see com.yyoa.testData.BaseDaoTest.launcher.util.BaseDao#saveOrUpdate(java.lang.Object)
	 */
	@Override
	public void saveOrUpdate(T t) {
		this.getSession().saveOrUpdate(t);
	}

	/**
	 * <load> <加载实体的load方法>
	 * 
	 * @param id
	 *            实体的id
	 * @return 查询出来的实体
	 * @see com.yyoa.testData.BaseDaoTest.launcher.util.BaseDao#load(java.io.Serializable)
	 */
	@Override
	public T load(Long id) {
		T load = (T) this.getSession().load(getEntityClass(), id);
		return load;
	}

	/**
	 * <get> <查找的get方法>
	 * 
	 * @param id
	 *            实体的id
	 * @return 查询出来的实体
	 * @see com.yyoa.testData.BaseDaoTest.launcher.util.BaseDao#get(java.io.Serializable)
	 */
	@Override
	public T get(Long id) {
		T load = (T) this.getSession().get(getEntityClass(), id);
		return load;
	}

	/**
	 * <contains>
	 * 
	 * @param t
	 *            实体
	 * @return 是否包含
	 * @see com.yyoa.testData.BaseDaoTest.launcher.util.BaseDao#contains(java.lang.Object)
	 */
	@Override
	public boolean contains(T t) {
		return this.getSession().contains(t);
	}

	/**
	 * <delete> <删除表中的t数据>
	 * 
	 * @param t
	 *            实体
	 * @see com.yyoa.testData.BaseDaoTest.launcher.util.BaseDao#delete(java.lang.Object)
	 */
	@Override
	public void delete(T t) {
		this.getSession().delete(t);
	}

	/**
	 * <根据ID删除数据>
	 * 
	 * @param Id
	 *            实体id
	 * @return 是否删除成功
	 * @see com.yyoa.testData.BaseDaoTest.launcher.util.BaseDao#deleteById(java.io.Serializable)
	 */
	@Override
	public boolean deleteById(Long Id) {
		T t = get(Id);
		if (t == null) {
			return false;
		}
		delete(t);
		return true;
	}

	/**
	 * <删除所有>
	 * 
	 * @param entities
	 *            实体的Collection集合
	 * @see com.yyoa.testData.BaseDaoTest.launcher.util.BaseDao#deleteAll(java.util.Collection)
	 */
	@Override
	public void deleteAll(Collection<T> entities) {
		for (Object entity : entities) {
			this.getSession().delete(entity);
		}
	}

	/**
	 * <执行Hql语句>
	 * 
	 * @param hqlString
	 *            hql
	 * @param values
	 *            不定参数数组
	 * @see com.yyoa.testData.BaseDaoTest.launcher.util.BaseDao#queryHql(java.lang.String,
	 *      java.lang.Object[])
	 */
	@Override
	public void queryHql(String hqlString, Object... values) {
		Query query = this.getSession().createQuery(hqlString);
		if (values != null) {
			for (int i = 0; i < values.length; i++) {
				query.setParameter(i, values[i]);
			}
		}
		query.executeUpdate();
	}

	/**
	 * <执行Sql语句>
	 * 
	 * @param sqlString
	 *            sql
	 * @param values
	 *            不定参数数组
	 * @see com.yyoa.testData.BaseDaoTest.launcher.util.BaseDao#querySql(java.lang.String,
	 *      java.lang.Object[])
	 */
	@Override
	public void querySql(String sqlString, Object... values) {
		Query query = this.getSession().createSQLQuery(sqlString);
		if (values != null) {
			for (int i = 0; i < values.length; i++) {
				query.setParameter(i, values[i]);
			}
		}
		query.executeUpdate();
	}

	/**
	 * <根据HQL语句查找唯一实体>
	 * 
	 * @param hqlString
	 *            HQL语句
	 * @param values
	 *            不定参数的Object数组
	 * @return 查询实体
	 * @see com.yyoa.testData.BaseDaoTest.launcher.util.BaseDao#getByHQL(java.lang.String,
	 *      java.lang.Object[])
	 */
	@Override
	public T getByHQL(String hqlString, Object... values) {
		Query query = this.getSession().createQuery(hqlString);
		if (values != null) {
			for (int i = 0; i < values.length; i++) {
				query.setParameter(i, values[i]);
			}
		}
		return (T) query.uniqueResult();
	}

	/**
	 * <根据SQL语句查找唯一实体>
	 * 
	 * @param sqlString
	 *            SQL语句
	 * @param values
	 *            不定参数的Object数组
	 * @return 查询实体
	 * @see com.yyoa.testData.BaseDaoTest.launcher.util.BaseDao#getBySQL(java.lang.String,
	 *      java.lang.Object[])
	 */
	@Override
	public T getBySQL(String sqlString, Object... values) {
		Query query = this.getSession().createSQLQuery(sqlString);
		if (values != null) {
			for (int i = 0; i < values.length; i++) {
				query.setParameter(i, values[i]);
			}
		}
		return (T) query.uniqueResult();
	}

	/**
	 * <根据HQL语句，得到对应的list>
	 * 
	 * @param hqlString
	 *            HQL语句
	 * @param values
	 *            不定参数的Object数组
	 * @return 查询多个实体的List集合
	 * @see com.yyoa.testData.BaseDaoTest.launcher.util.BaseDao#getListByHQL(java.lang.String,
	 *      java.lang.Object[])
	 */
	@Override
	public List<T> getListByHQL(String hqlString, Object... values) {
		Query query = this.getSession().createQuery(hqlString);
		if (values != null) {
			for (int i = 0; i < values.length; i++) {
				query.setParameter(i, values[i]);
			}
		}
		return query.list();
	}

	/**
	 * <根据SQL语句，得到对应的list>
	 * 
	 * @param sqlString
	 *            HQL语句
	 * @param values
	 *            不定参数的Object数组
	 * @return 查询多个实体的List集合
	 * @see com.yyoa.testData.BaseDaoTest.launcher.util.BaseDao#getListBySQL(java.lang.String,
	 *      java.lang.Object[])
	 */
	@Override
	public List<T> getListBySQL(String sqlString, Object... values) {
		Query query = this.getSession().createSQLQuery(sqlString);
		if (values != null) {
			for (int i = 0; i < values.length; i++) {
				query.setParameter(i, values[i]);
			}
		}
		return query.list();
	}

	/**
	 * <refresh>
	 * 
	 * @param t
	 *            实体
	 * @see com.yyoa.testData.BaseDaoTest.launcher.util.BaseDao#refresh(java.lang.Object)
	 */
	@Override
	public void refresh(T t) {
		this.getSession().refresh(t);
	}

	/**
	 * <update>
	 * 
	 * @param t
	 *            实体
	 * @see com.yyoa.testData.BaseDaoTest.launcher.util.BaseDao#update(java.lang.Object)
	 */
	@Override
	public void update(T t) {
		this.getSession().update(t);
	}

	/**
	 * <根据HQL得到记录数>
	 * 
	 * @param hql
	 *            HQL语句
	 * @param values
	 *            不定参数的Object数组
	 * @return 记录总数
	 * @see com.yyoa.testData.BaseDaoTest.launcher.util.BaseDao#countByHql(java.lang.String,
	 *      java.lang.Object[])
	 */
	@Override
	public Long countByHql(String hql, Object... values) {
		Query query = this.getSession().createQuery(hql);
		if (values != null) {
			for (int i = 0; i < values.length; i++) {
				query.setParameter(i, values[i]);
			}
		}
		return (Long) query.uniqueResult();
	}

	/**
	 * 按Criteria分页查询.
	 * 
	 * @param page
	 *            分页参数.
	 * @param criterions
	 *            数量可变的Criterion.
	 * 
	 * @return 分页查询结果.附带结果列表及所有查询输入参数.
	 */
	public Page<T> findPage(final Page<T> page, final Criterion... criterions) {
		Assert.notNull(page, "page不能为空");

		Criteria c = createCriteria(criterions);

		if (page.isAutoCount()) {
			long totalCount = countCriteriaResult(c);
			page.setTotalCount(totalCount);
		}

		setPageParameterToCriteria(c, page);

		List result = c.list();
		page.setResult(result);
		return page;
	}

	/**
	 * 设置分页参数到Criteria对象,辅助函数.
	 */
	protected Criteria setPageParameterToCriteria(final Criteria c,
			final Page<T> page) {

		Assert.isTrue(page.getPageSize() > 0, "Page Size must larger than zero");

		// hibernate的firstResult的序号从0开始
		c.setFirstResult(page.getFirst() - 1);
		c.setMaxResults(page.getPageSize());

		if (page.isOrderBySetted()) {
			String[] orderByArray = StringUtils.split(page.getOrderBy(), ',');
			String[] orderArray = StringUtils.split(page.getOrder(), ',');

			Assert.isTrue(orderByArray.length == orderArray.length,
					"分页多重排序参数中,排序字段与排序方向的个数不相等");

			for (int i = 0; i < orderByArray.length; i++) {
				if (Page.ASC.equals(orderArray[i])) {
					c.addOrder(Order.asc(orderByArray[i]));
				} else {
					c.addOrder(Order.desc(orderByArray[i]));
				}
			}
		}
		return c;
	}

	/**
	 * 执行count查询获得本次Criteria查询所能获得的对象总数.
	 */
	protected long countCriteriaResult(final Criteria c) {
		CriteriaImpl impl = (CriteriaImpl) c;

		// 先把Projection、ResultTransformer、OrderBy取出来,清空三者后再执行Count操作
		Projection projection = impl.getProjection();
		ResultTransformer transformer = impl.getResultTransformer();

		List<CriteriaImpl.OrderEntry> orderEntries = null;
		try {
			orderEntries = (List) ReflectionUtils.getFieldValue(impl,
					"orderEntries");
			ReflectionUtils
					.setFieldValue(impl, "orderEntries", new ArrayList());
		} catch (Exception e) {
			logger.error("不可能抛出的异常:{}", e.getMessage());
		}

		// 执行Count查询
		Long totalCountObject = (Long) c.setProjection(Projections.rowCount())
				.uniqueResult();
		long totalCount = (totalCountObject != null) ? totalCountObject : 0;

		// 将之前的Projection,ResultTransformer和OrderBy条件重新设回去
		c.setProjection(projection);

		if (projection == null) {
			c.setResultTransformer(CriteriaSpecification.ROOT_ENTITY);
		}
		if (transformer != null) {
			c.setResultTransformer(transformer);
		}
		try {
			ReflectionUtils.setFieldValue(impl, "orderEntries", orderEntries);
		} catch (Exception e) {
			logger.error("不可能抛出的异常:{}", e.getMessage());
		}

		return totalCount;
	}

	public Criteria createCriteria() {
		return getSession().createCriteria(getEntityClass());
	}

	/**
	 * 根据Criterion条件创建Criteria. 与find()函数可进行更加灵活的操作.
	 * 
	 * @param criterions
	 *            数量可变的Criterion.
	 */
	public Criteria createCriteria(final Criterion... criterions) {
		Criteria criteria = createCriteria();
		for (Criterion c : criterions) {
			criteria.add(c);
		}
		return criteria;
	}

	// // findPage时，级联查询子表
	// @Override
	// public Criteria createCriteria(final Criterion... criterions) {
	// Criteria criteria =
	// getSession().createCriteria(entityClass).createAlias("user", "user");
	// for (Criterion c : criterions) {
	// criteria.add(c);
	// }
	// return criteria;
	// }
	@Override
	public Page<T> findPage(Page<T> page, Constraint constraint) {
		Criterion criterion = buildCriterion(constraint);
		if (criterion == null) {
			return findPage(page);
		}
		return findPage(page, criterion);
	}

	private Criterion buildCriterion(Constraint constraint) {
		if (constraint == null) {
			return null;
		}
		if (constraint instanceof SimpleConstraint) {
			SimpleConstraint simpleConstraint = (SimpleConstraint) constraint;
			Criterion criterion = buildCriterionDetail(simpleConstraint);
			return criterion;
		} else {
			ConstraintGroup constraintGroup = (ConstraintGroup) constraint;
			Junction junction = constraintGroup.getLogicOperator().equals(
					ConstraintGroup.LOGIC_AND) ? Restrictions.conjunction()
					: Restrictions.disjunction();
			for (Constraint c : constraintGroup.getConstraints()) {
				Criterion buildCriterion = buildCriterion(c);
				if (buildCriterion != null) {
					junction.add(buildCriterion);
				}
			}
			return junction;
		}
	}

	/**
	 * 按属性条件参数创建Criterion,辅助函数.
	 */
	@SuppressWarnings("deprecation")
	protected Criterion buildCriterionDetail(SimpleConstraint simpleConstraint) {
		String entityProperty = simpleConstraint.getEntityProperty();
		String compareOperator = simpleConstraint.getCompareOperator();
		Object value = simpleConstraint.getValue();
		Assert.hasText(entityProperty, "entityProperty不能为空");
		Criterion criterion = null;
		// 根据MatchType构造criterion
		switch (compareOperator) {
		case SimpleConstraint.COMPARE_EQUAL:
			criterion = Restrictions.eq(entityProperty, value);
			break;
		case SimpleConstraint.COMPARE_NOT_EQUAL:
			criterion = Restrictions.ne(entityProperty, value);
			break;
		case SimpleConstraint.COMPARE_LIKE_ANYWHERE:
			criterion = Restrictions.like(entityProperty, (String) value,
					MatchMode.ANYWHERE);
			break;
		case SimpleConstraint.COMPARE_LIKE_END:
			criterion = Restrictions.like(entityProperty, (String) value,
					MatchMode.END);
			break;
		case SimpleConstraint.COMPARE_LIKE_EXACT:
			criterion = Restrictions.like(entityProperty, (String) value,
					MatchMode.EXACT);
			break;
		case SimpleConstraint.COMPARE_LIKE_START:
			criterion = Restrictions.like(entityProperty, (String) value,
					MatchMode.START);
			break;
		case SimpleConstraint.COMPARE_NOT_LIKE_ANYWHERE:
			criterion = Restrictions.not(Restrictions.like(entityProperty,
					(String) value, MatchMode.ANYWHERE));
			break;
		case SimpleConstraint.COMPARE_NOT_LIKE_END:
			criterion = Restrictions.not(Restrictions.like(entityProperty,
					(String) value, MatchMode.END));
			break;
		case SimpleConstraint.COMPARE_NOT_LIKE_EXACT:
			criterion = Restrictions.not(Restrictions.like(entityProperty,
					(String) value, MatchMode.EXACT));
			break;
		case SimpleConstraint.COMPARE_NOT_LIKE_START:
			criterion = Restrictions.not(Restrictions.like(entityProperty,
					(String) value, MatchMode.START));
			break;
		case SimpleConstraint.COMPARE_SMALLER_EQUAL:
			criterion = Restrictions.le(entityProperty, value);
			break;
		case SimpleConstraint.COMPARE_SMALLER:
			criterion = Restrictions.lt(entityProperty, value);
			break;
		case SimpleConstraint.COMPARE_GREATER_EQUAL:
			criterion = Restrictions.ge(entityProperty, value);
			break;
		case SimpleConstraint.COMPARE_GREATER:
			criterion = Restrictions.gt(entityProperty, value);
			break;
		case SimpleConstraint.COMPARE_IS_NULL:
			criterion = Restrictions.isNull(entityProperty);
			break;
		case SimpleConstraint.VALUE_NOT_NULL:
			criterion = Restrictions.isNotNull(entityProperty);
			break;
		case SimpleConstraint.VALUE_NULL:
			criterion = Restrictions.isNull(entityProperty);
			break;
		default:
			throw new IllegalStateException("compareOperator '"
					+ compareOperator + "' is not support yet");
		}
		return criterion;
	}

	/**
	 * @return the sessionFactory
	 */
	public SessionFactory getSessionFactory() {
		return sessionFactory;
	}

	/**
	 * @param sessionFactory
	 *            the sessionFactory to set
	 */
	public void setSessionFactory(SessionFactory sessionFactory) {
		this.sessionFactory = sessionFactory;
	}

	/**
	 *
	 * @return session
	 */
	public Session getSession() {
		// 需要开启事物，才能得到CurrentSession
		return sessionFactory.getCurrentSession();
	}
}
